package twitter

import (
	"container/list"
	"fmt"
	"sync"
)

type MessageQueue struct {
	queue *list.List
	deDup map[interface{}]struct{}
	rw    sync.Mutex
}

func NewMessageQueue() *MessageQueue {
	return &MessageQueue{
		queue: list.New(),
		deDup: make(map[interface{}]struct{}),
	}
}

func (mq *MessageQueue) PushMany(message []interface{}) {

	mq.rw.Lock()
	defer mq.rw.Unlock()

	for _, v := range message {
		if _, ok := mq.deDup[v]; ok {
			continue
		} else {
			mq.deDup[v] = struct{}{}
		}
		mq.queue.PushBack(v)
	}
	return
}

func (mq *MessageQueue) Push(message interface{}) {
	mq.rw.Lock()
	defer mq.rw.Unlock()

	if _, ok := mq.deDup[message]; ok {
		return
	} else {
		mq.deDup[message] = struct{}{}
	}
	mq.queue.PushBack(message)
}

func (mq *MessageQueue) Pop() (interface{}, int, error) {
	mq.rw.Lock()
	defer mq.rw.Unlock()

	if mq.queue.Len() == 0 {
		return nil, 0, fmt.Errorf("queue is empty")
	}

	element := mq.queue.Front()
	if element != nil {
		mq.queue.Remove(element)
	}

	delete(mq.deDup, element)

	return element.Value, mq.queue.Len(), nil
}

func (mq *MessageQueue) PopWithCallBack(fCheckValid func(data interface{}, size int) bool) (interface{}, int, error) {

	mq.rw.Lock()
	defer mq.rw.Unlock()

	size := mq.queue.Len()
	if size == 0 {
		return nil, 0, fmt.Errorf("queue is empty")
	}

	element := mq.queue.Front()
	if element != nil {
		mq.queue.Remove(element)
	}

	delete(mq.deDup, element)

	if b := fCheckValid(element.Value, size); b {

		mq.deDup[element.Value] = struct{}{}
		mq.queue.PushBack(element.Value)
	} else {
		return nil, mq.queue.Len(), nil
	}

	return element.Value, mq.queue.Len(), nil
}

func (mq *MessageQueue) Seek() (interface{}, error) {
	mq.rw.Lock()
	defer mq.rw.Unlock()

	if mq.queue.Len() == 0 {
		return nil, fmt.Errorf("queue is empty")
	}

	element := mq.queue.Front()
	if element != nil {
		mq.queue.MoveToBack(element)
	}

	return element.Value, nil
}

func (mq *MessageQueue) Size() int {
	mq.rw.Lock()
	defer mq.rw.Unlock()

	return mq.queue.Len()
}
